﻿// <copyright file="EntityFrameworkViewsTemplate.cs" company="T4 Toolbox Team">
//  Copyright © T4 Toolbox Team. All Rights Reserved.
// </copyright>

namespace T4Toolbox.EntityFramework
{
    using System;
    using System.Collections.Generic;
    using System.Data.Entity.Design;
    using System.Data.Mapping;
    using System.Data.Metadata.Edm;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.IO;
    using System.Xml;

    /// <summary>
    /// Generates Entity Framework views.
    /// </summary>
    public class EntityFrameworkViewsTemplate : Template
    {
        /// <summary>
        /// Gets or sets the Entity Framework Conceptual Model (CSDL).
        /// </summary>
        /// <value>
        /// A <see cref="String"/> that contains generated CSDL in XML form.
        /// </value>
        public string ConceptualModel { get; set; }

        /// <summary>
        /// Gets or sets the programming language for generating entity views.
        /// </summary>
        /// <value>
        /// A <see cref="LanguageOption"/> value, 
        /// <see cref="System.Data.Entity.Design.LanguageOption.GenerateCSharpCode"/> or
        /// <see cref="System.Data.Entity.Design.LanguageOption.GenerateVBCode"/>.
        /// </value>
        public LanguageOption LanguageOption { get; set; }

        /// <summary>
        /// Gets or sets Entity Framework Mapping Model (MSL).
        /// </summary>
        /// <value>
        /// A <see cref="String"/> that contains generated MSL in XML form.
        /// </value>
        public string MappingModel { get; set; }

        /// <summary>
        /// Gets or sets Entity Framework Storage Model (SSDL).
        /// </summary>
        /// <value>
        /// A <see cref="String"/> that contains generated SSDL in XML form.
        /// </value>
        public string StorageModel { get; set; }

        /// <summary>
        /// Generates Entity Framework views.
        /// </summary>
        /// <returns>
        /// C# or Visual Basic code with generated views.
        /// </returns>
        [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "stringReader is not disposed by xmlReader")]
        public override string TransformText()
        {
            // Re-hydrate conceptual model
            EdmItemCollection conceptualItems;
            using (StringReader stringReader = new StringReader(this.ConceptualModel))
            using (XmlReader xmlReader = XmlReader.Create(stringReader, new XmlReaderSettings() { CloseInput = false }))
            {
                conceptualItems = new EdmItemCollection(new XmlReader[] { xmlReader });
            }

            // Re-hydrate storage model
            StoreItemCollection storageItems;
            using (StringReader stringReader = new StringReader(this.StorageModel))
            using (XmlReader xmlReader = XmlReader.Create(stringReader, new XmlReaderSettings() { CloseInput = false }))
            {
                storageItems = new StoreItemCollection(new XmlReader[] { xmlReader });
            }

            // Re-hydrate mapping model
            StorageMappingItemCollection mappingItems;
            using (StringReader stringReader = new StringReader(this.MappingModel))
            using (XmlReader xmlReader = XmlReader.Create(stringReader, new XmlReaderSettings() { CloseInput = false }))
            {
                mappingItems = new StorageMappingItemCollection(conceptualItems, storageItems, new XmlReader[] { xmlReader });
            }

            // Generate views code
            using (StringWriter stringWriter = new StringWriter(this.GenerationEnvironment, CultureInfo.CurrentCulture))
            {
                EntityViewGenerator generator = new EntityViewGenerator(this.LanguageOption);
                IEnumerable<EdmSchemaError> errors = generator.GenerateViews(mappingItems, stringWriter);
                EntityFrameworkGenerator.HandleErrors(errors, this.Errors);
            }

            return this.GenerationEnvironment.ToString();
        }

        /// <summary>
        /// Validates properties before code generation.
        /// </summary>
        protected override void Validate()
        {
            if (string.IsNullOrEmpty(this.ConceptualModel))
            {
                throw new TransformationException("ConceptualModel property must be assigned");
            }

            if (string.IsNullOrEmpty(this.MappingModel))
            {
                throw new TransformationException("MappingModel property must be assigned");
            }

            if (string.IsNullOrEmpty(this.StorageModel))
            {
                throw new TransformationException("StorageModel property must be assigned");
            }
        }
    }
}
